﻿//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Common Core Extensions
//===============================================================================

using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using Microsoft.Practices.EnterpriseLibrary.Configuration.Design;
using EntLibContrib.Common.Properties;

namespace EntLibContrib.Common.Configuration.Design.Editors
{
	/// <summary>
	/// Provides a user interface for seleting a <see cref="TypeMethodName"/>.
	/// </summary>
	/// <remarks>
	/// <see cref="MethodSelectorEditor"/> is a <see cref="UITypeEditor"/> that provides a dialog box for selecting a <see cref="Type"/> and a Method.
	/// </remarks>
	[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
	[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
	public class MethodSelectorEditor : UITypeEditor
	{
		#region Construction
		/// <summary>
		/// Initialize a new instance of the <see cref="MethodSelectorEditor"/> class.
		/// </summary>
		public MethodSelectorEditor()
			: base()
		{
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Edits the specified object's value using the editor style indicated by <seealso cref="UITypeEditor.GetEditStyle()"/>.
		/// </summary>
		/// <param name="context">
		/// An <see cref="ITypeDescriptorContext"/> that can be used to gain additional context information.
		/// </param>
		/// <param name="provider">
		/// An <see cref="IServiceProvider"/> that this editor can use to obtain services.
		/// </param>
		/// <param name="value">
		/// The object to edit.
		/// </param>
		/// <returns>
		/// The fully qualifed type name for the chosen type.
		/// </returns>
		public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
		{
			if (context != null && provider != null)
			{
				IWindowsFormsEditorService editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
				if (editorService != null)
				{
					TypeMethodName currentTypeMethodName = (TypeMethodName)value;
					BaseTypeAttribute baseTypeAttribute = LocateBaseType(context);
					MethodFilterAttribute methodFilterAttribute = LocateMethodFilter(context);
					using (MethodSelectorUI form = new MethodSelectorUI(currentTypeMethodName, baseTypeAttribute, methodFilterAttribute))
					{
						if (editorService.ShowDialog(form) == DialogResult.OK)
						{
							if (form.SelectedMethod != null)
								currentTypeMethodName = form.SelectedMethod;
						}
					}
					return currentTypeMethodName;
				}
			}

			return base.EditValue(context, provider, value);
		}

		/// <summary>
		/// Gets the editor style used by the <seealso cref="UITypeEditor.EditValue(ITypeDescriptorContext, IServiceProvider, object)"/> method.
		/// </summary>
		/// <param name="context">An <see cref="ITypeDescriptorContext"/> that can be used to gain additional context information</param>
		/// <returns>
		/// 	<see cref="UITypeEditorEditStyle.Modal"/> for this editor.
		/// </returns>
		public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
		{
			return UITypeEditorEditStyle.Modal;
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Locate the base type of the object to use to filter possible types.
		/// </summary>        
		private static BaseTypeAttribute LocateBaseType(ITypeDescriptorContext context)
		{
			BaseTypeAttribute baseTypeAttribute = null;
			foreach (Attribute attribute in context.PropertyDescriptor.Attributes)
			{
				baseTypeAttribute = attribute as BaseTypeAttribute;
				if (null != baseTypeAttribute)
					break;
			}

			if (baseTypeAttribute == null)
				throw new InvalidOperationException(InternalResources.ExceptionNoBaseTypeAttribute);

			return baseTypeAttribute;
		}

		/// <summary>
		/// Locate the method filter to use to filter possible method.
		/// </summary>        
		private static MethodFilterAttribute LocateMethodFilter(ITypeDescriptorContext context)
		{
			MethodFilterAttribute methodFilterAttribute = null;
			foreach (Attribute attribute in context.PropertyDescriptor.Attributes)
			{
				methodFilterAttribute = attribute as MethodFilterAttribute;
				if (null != methodFilterAttribute)
					break;
			}

			if (methodFilterAttribute == null)
				methodFilterAttribute = new MethodFilterAttribute();

			return methodFilterAttribute;
		}
		#endregion
	}
}
